package ch.ethz.fcl.metrobuzz.data.ltt;

import java.util.ArrayList;
import java.util.List;

import ch.ethz.fcl.metrobuzz.algorithm.list.ListOperation;
import ch.ethz.fcl.metrobuzz.data.ltt.node.LTTNode;

public class LongTermTrajectory {
	private List<LTTNode> nodeList; // passing nodes
	private List<Integer> trajIndexList; // list of trajectory indices

	public LongTermTrajectory() {
		init(null);
	}

	public LongTermTrajectory(List<LTTNode> list) {
		init(list);
	}

	private void init(List<LTTNode> nodes) {
		this.nodeList = new ArrayList<LTTNode>();
		this.trajIndexList = new ArrayList<Integer>();
		if (nodes != null)
			nodeList.addAll(nodes);
	}

	public void addNode(LTTNode node) {
		if (nodeList.contains(node))
			return;
		nodeList.add(node);
	}

	public void addNode(LTTNode node, int index) {
		if (nodeList.contains(node))
			return;
		if (index == -1)
			nodeList.add(node);
		else
			nodeList.add(index, node);
	}

	public void addTrajIndex(int index) {
		trajIndexList.add(index);
	}

	public void addTransIndices(List<Integer> indices) {
		trajIndexList.addAll(indices);
	}

	public List<LTTNode> getNodeList() {
		return this.nodeList;
	}

	public int nodeSize() {
		return nodeList.size();
	}

	public List<Integer> getTrajIndexList() {
		return this.trajIndexList;
	}

	public int frequency() {
		return trajIndexList.size();
	}

	public String toString() {
		String output = "";
		int i = 0;
		for (; i < nodeList.size() - 1; i++)
			output += nodeList.get(i).toNode().getId() + "->";
		output += nodeList.get(i).toNode().getId() + ": " + frequency();
		return output;
	}

	@Override
	public boolean equals(Object o) {
		if (!(o instanceof LongTermTrajectory))
			return false;

		LongTermTrajectory ltt = (LongTermTrajectory) o;
		if (nodeSize() != ltt.nodeSize())
			return false;

		for (int i = 0; i < nodeSize(); i++)
			if (!nodeList.get(i).equals(ltt.nodeList.get(i)))
				return false;
		return true;
	}

	public boolean contains(LongTermTrajectory anotherTraj) {
		if (anotherTraj.nodeSize() > nodeSize())
			return false;
		
		int firstTransNodeIndex = nodeList.indexOf(anotherTraj.getNodeList()
				.get(0));

		if (firstTransNodeIndex == -1)
			return false;

		if (anotherTraj.nodeSize() + firstTransNodeIndex > nodeSize())
			return false;

		for (int i = 0; i < anotherTraj.nodeSize(); i++)
			if (!nodeList.get(i + firstTransNodeIndex).equals(
					anotherTraj.getNodeList().get(i)))
				return false;
		return true;
	}

	public LongTermTrajectory combines(LongTermTrajectory antl2) {
		LongTermTrajectory antl1 = this;

		if (antl1 == null || antl1.nodeSize() == 0 || antl2 == null
				|| antl2.nodeSize() == 0)
			return null;

		LongTermTrajectory a1 = antl1, a2 = antl2;
		if (a2.getNodeList().indexOf(a1.getNodeList().get(0)) != -1) {
			a1 = antl2;
			a2 = antl1;
		}

		int size1 = a1.nodeSize(), size2 = a2.nodeSize();

		int a2StartInA1 = a1.getNodeList().indexOf(a2.getNodeList().get(0));
		if (a2StartInA1 == -1)
			return null;

		int a1EndInA2 = a2.getNodeList().indexOf(
				a1.getNodeList().get(size1 - 1));
		if (a1EndInA2 == -1 || a1EndInA2 == 0)
			return null;

		if (a2.getNodeList().indexOf(a1.getNodeList().get(size1 - 1)) == -1)
			return null;

		if (size2 < size1 - a2StartInA1)
			return null;

		LongTermTrajectory combine = new LongTermTrajectory();
		for (int i = 0; i < size1; i++) {
			if (i >= a2StartInA1
					&& !a1.getNodeList().get(i)
							.equals(a2.getNodeList().get(i - a2StartInA1)))
				return null;
			combine.addNode(a1.getNodeList().get(i));
		}

		for (int i = a1EndInA2 + 1; i < size2; i++)
			combine.addNode(a2.getNodeList().get(i), -1);

		combine.addTransIndices(ListOperation.unionWithoutDuplicates(
				antl1.getTrajIndexList(), antl2.getTrajIndexList()));
		return combine;
	}
}
